package com.gitee.huanminabc.jcommon.encryption;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * JWT 工具类，支持 access/refresh/custom 三种 token 生成与解析。
 * 线程安全，密钥与过期时间通过构造函数注入。
 */
@Data
public class JwtUtil {
    // claim key 常量
    private static final String CLAIM_PASS = "pass";
    private static final String CLAIM_CREATE_JWT_TIME = "create_JWT_Time";

    private final String accessKey;
    private final String refreshKey;
    private final long accessExpireMillis;
    private final long refreshExpireMillis;
    private final long customExpireMillis;

    public enum JwtEnum {
        ACCESS, REFRESH, CUSTOM
    }

    /**
     * 构造函数，注入密钥和过期时间
     */
    public JwtUtil(String accessKey, String refreshKey, long accessExpireMillis, long refreshExpireMillis, long customExpireMillis) {
        this.accessKey = Objects.requireNonNull(accessKey, "accessKey不能为空");
        this.refreshKey = Objects.requireNonNull(refreshKey, "refreshKey不能为空");
        this.accessExpireMillis = accessExpireMillis;
        this.refreshExpireMillis = refreshExpireMillis;
        this.customExpireMillis = customExpireMillis;
    }

    /**
     * 生成 JWT
     * @param id 用户id
     * @param username 用户名
     * @param password 用户密码
     * @param claims 额外自定义 claims
     * @param type token 类型
     * @return JWT 字符串
     */
    public String createJWT(String id, String username, String password, Map<String, Object> claims, JwtEnum type) {
        Objects.requireNonNull(type, "JwtEnum不能为空");
        Objects.requireNonNull(username, "username不能为空");
        Objects.requireNonNull(password, "password不能为空");
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        // 合并 claims
        Map<String, Object> allClaims = new HashMap<>();
        if (claims != null) allClaims.putAll(claims);
        allClaims.put(CLAIM_PASS, password);

        JwtBuilder builder = Jwts.builder()
                .setClaims(allClaims)
                .setSubject(username)
                .setIssuedAt(now)
                .signWith(SignatureAlgorithm.HS256, getKey(type));
        if (id != null) builder.setId(id);

        // 过期时间
        long expireMillis;
        if (type == JwtEnum.ACCESS) {
            expireMillis = accessExpireMillis;
        } else if (type == JwtEnum.REFRESH) {
            expireMillis = refreshExpireMillis;
        } else { // CUSTOM
            Object customTime = claims != null ? claims.get(CLAIM_CREATE_JWT_TIME) : null;
            expireMillis = (customTime instanceof Long) ? (Long) customTime : customExpireMillis;
        }
        builder.setExpiration(new Date(nowMillis + expireMillis));
        return builder.compact();
    }

    /**
     * 生成 JWT（无自定义 claims）
     */
    public String createJWT(String username, String password, JwtEnum type) {
        return createJWT(null, username, password, null, type);
    }

    /**
     * 生成 custom 类型 JWT，指定过期时间
     */
    public String createJWT(String username, String password, JwtEnum type, long customExpireMillis) {
        if (type != JwtEnum.CUSTOM) {
            throw new IllegalArgumentException("customExpireMillis 仅适用于 CUSTOM 类型");
        }
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_CREATE_JWT_TIME, customExpireMillis);
        return createJWT(null, username, password, claims, type);
    }

    /**
     * 解析 JWT，返回 Claims
     * @param jwtStr token 字符串
     * @param type token 类型
     * @return Claims
     * @throws io.jsonwebtoken.JwtException token 无效或过期时抛出
     */
    public Claims parseJWT(String jwtStr, JwtEnum type) {
        Objects.requireNonNull(type, "JwtEnum不能为空");
        Objects.requireNonNull(jwtStr, "jwtStr不能为空");
        return Jwts.parser()
                .setSigningKey(getKey(type))
                .parseClaimsJws(jwtStr)
                .getBody();
    }

    /**
     * 从 token 获取用户名和密码
     */
    public Map<String, String> getUserNameAndPassFromToken(String token, JwtEnum type) {
        Claims claims = parseJWT(token, type);
        Map<String, String> map = new HashMap<>();
        map.put("user", claims.getSubject());
        map.put("pass", String.valueOf(claims.get(CLAIM_PASS)));
        return map;
    }

    /**
     * 从 token 获取过期时间
     */
    public Date getExpirationDateFromToken(String token, JwtEnum type) {
        try {
            return parseJWT(token, type).getExpiration();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 从 token 获取用户名
     */
    public String getUserNameFromToken(String token, JwtEnum type) {
        return parseJWT(token, type).getSubject();
    }

    /**
     * 从 token 获取用户 id
     */
    public String getUserIdFromToken(String token, JwtEnum type) {
        return parseJWT(token, type).getId();
    }

    /**
     * 获取签名密钥
     */
    private String getKey(JwtEnum type) {
        switch (type) {
            case ACCESS: return accessKey;
            case REFRESH: return refreshKey;
            case CUSTOM: return accessKey;
            default: throw new IllegalArgumentException("未知的 JwtEnum 类型");
        }
    }

}
